package Servicios;

import DAO.*;
import VO.*;
import Entidad.*;
import Singletons.Mensajes;
import Singletons.Restricciones;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;

public class ServicioReservaSala {

    /**
     * <p>Busca y retorna las reservas de la sala cuyo id sea idSala y la fecha sea la indicada en Date</p>
     * @param idSala Long con el id de la sala cuyas reservas se requieren
     * @param fecha Date con la fecha de las reservas que se necesitan
     * @param em EntityManager que se conecta con la base de datos
     * @return lista de ReservaSalaVO de esa sala en esa fecha
     */
    public ArrayList<ReservaSalaVO> getReservasSala(long idSala,Date fecha,EntityManager em){
        ArrayList<ReservaSala> reservasSala=new ReservaSalaDAO().listaReservasSala(idSala,em);
        ArrayList<ReservaSalaVO> reservasSalaFecha=new ArrayList<ReservaSalaVO>();
        SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");
        String stringFecha=dateFormat.format(fecha);
        for(ReservaSala reservaSala:reservasSala)
            if(dateFormat.format(reservaSala.getFecha()).equals(stringFecha))
                reservasSalaFecha.add(reservaSala_a_VO(reservaSala));
        return reservasSalaFecha;
    }

    /**
     * <p>Busca y retorna las reservas de estaciones de la sala cuyo id sea idSala y la fecha sea la indicada en Date</p>
     * @param idSala Long con el id de la sala cuyas reservas de estacion se requieren
     * @param fecha Date con la fecha de las reservas que se necesitan
     * @param em EntityManager que se conecta con la base de datos
     * @return lista de ReservaEstacionVO de las estaciones de esa sala en esa fecha
     */
    public ArrayList<ReservaEstacionVO> getReservasEstacionSala(long idSala,Date fecha,EntityManager em){
        ArrayList<ReservaEstacion> reservasEstacionSala=new ReservaEstacionDAO().listaReservasEstacionPorSala(idSala,em);
        ArrayList<ReservaEstacionVO> reservasEstacionSalaFecha=new ArrayList<ReservaEstacionVO>();
        SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");
        String stringFecha=dateFormat.format(fecha);
        for(ReservaEstacion reservaEstacion:reservasEstacionSala)
            if(dateFormat.format(reservaEstacion.getFecha()).equals(stringFecha))
                reservasEstacionSalaFecha.add(reservaEstacion_a_VO(reservaEstacion));
        return reservasEstacionSalaFecha;
    }

    /**
     * <p>Busca y retorna una reserva a nombre de un profesor en una fecha y hora indicada.</p>
     * @param profesor String con el nombre del profesor
     * @param horaInicio String con la hora en formato hh:mm
     * @param fecha Date
     * @param em EntityManager que se conecta con la base de datos
     * @return una reserva de sala que cumpla con las condiciones, o null si no existe tal
     */
    public ReservaSalaVO getReservaSala(String profesor,String horaInicio,Date fecha,EntityManager em){
        horaInicio=horaInicio.substring(0,2);
        ArrayList<ReservaSala> reservasSalaProf=new ReservaSalaDAO().reservasPendientesProfesor(profesor,em);
        SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");
        String stringFecha=dateFormat.format(fecha);
        for(ReservaSala reserva:reservasSalaProf)
            if(dateFormat.format(reserva.getFecha()).equals(stringFecha) && reserva.getHorarioReserva().substring(0,2).equals(horaInicio))
                return reservaSala_a_VO(reserva);
        return null;
    }

    /**
     * <p>Elimina una reserva de sala cuyo id coincida con el pasado como argumento</p>
     * @param id Long con el id de la reserva a eliminar
     * @param em EntityManager que se conecta con la base de datos
     */
    public void cancelarReservaSala(long id,EntityManager em){
        new ReservaSalaDAO().eliminar(id,em);
    }

    /**
     * <p>Contruye un objecto del tipo ReservaSala y le hace persistencia</p>
     * @param profesor String con el nombre del rpofesor que realiza la reserva
     * @param fecha Date con la fecha de la reserva
     * @param horario String con el horario de la reserva en formato hh:mm-hh:mm
     * @param idAdministador Long con el id del administrador de la sala
     * @param em EntityManager que se conecta con la base de datos
     * @return string con un mensaje de exito si se se guardo la ReservaSala, un mensaje de error si no
     */
    public String reservarSala(String profesor,Date fecha,String horario,long idAdministador,EntityManager em){
        AdministradorDAO adminDAO=new AdministradorDAO();
        Administrador admin=adminDAO.encontrar(idAdministador,em);
        if(admin==null)
            return Mensajes.ERROR_ADMINISTRADOR;
        Sala sala=admin.getSala();
        if(sala==null)
            return Mensajes.ERROR_SALA;
        ReservaSala reservaSala=new ReservaSala(admin,sala,horario,null,profesor,fecha);
        ReservaSalaDAO dao=new ReservaSalaDAO();
        dao.crear(reservaSala,em);
        return Mensajes.GUARDADO_RESERVA_CORRECTO;
    }

    /**
     * <p>Devuelve el horario de la salaVO dependiendo el valor dia</p>
     * @param dia Integer con numero el dia
     * @param salaVO SalaVO con la informacion de una sala
     * @return String con el horario correspondiente al dia
     */
    public String getHorario(int dia,SalaVO salaVO){
        if(dia!=7)
            return salaVO.getHorarioSemana();
        return salaVO.getHorarioSabado();
    }

    /**
     * <p>Compara las dos horas a ver si se solapan</p>
     * @param horario1 String con un valor de horario en el formato hh:mm-hh:mm
     * @param horario2 String con un valor de horario en el formato hh:mm-hh:mm
     * @return true si no se solapan las horas, false si si
     */
    public boolean horariosCompatibles(String horario1,String horario2){
        Hora h1Inicio=new Hora(horario1.substring(0,5));
        Hora h1Fin=new Hora(horario1.substring(6));
        Hora h2Inicio=new Hora(horario2.substring(0,5));
        Hora h2Fin=new Hora(horario2.substring(6));
        if(h2Inicio.esMenor(h1Fin) && h2Fin.esMayor(h1Inicio))
            return false;
        return true;
    }

    /**
     * <p>Compara dos horarios, uno de atencion y otro de solicitud, y analiza si
     * el de solicitud esta dentro del de atencion</p>
     * @param horarioAtencion String con un horario de atencion en formato hh:mm-hh:mm
     * @param horarioSolicitud String con un horario de solicitud en formato hh:mm-hh:mm
     * @return true si el horario de solicitud esta dentro del horario de atencion, false de lo contrario
     */
    public boolean horaPermitida(String horarioAtencion,String horarioSolicitud){
        Hora hAtencionInicio=new Hora(horarioAtencion.substring(0,5));
        Hora hAtencionFin=new Hora(horarioAtencion.substring(6));
        Hora hSolicitudInicio=new Hora(horarioSolicitud.substring(0,5));
        Hora hSolicitudFin=new Hora(horarioSolicitud.substring(6));
        if(hSolicitudInicio.esMenor(hAtencionInicio)||hAtencionFin.esMenor(hSolicitudFin))
            return false;
        return true;
    }

    /**
     * <p>Verifica si el entero pasado como argumento corresponde al dia domingo</p>
     * @param dia Integer con el numero del dia
     * @return true si dia es igual a 7
     */
    public boolean diaDomingo(int dia){
        return dia==1;
    }

    /**
     * <p>Verifica horario se solapa con alguno de los horarios de las reservas en las listas reservasSalaVO y reservasEstacionVO</p>
     * @param horario String con un horario en formato hh:mm-hh:mm
     * @param reservasSalaVO ArrayList<ReservaSalaVO>
     * @param reservasEstacionVO ArrayList<ReservaEstacionVO>
     * @return false si se solapan los horarios, true si no
     */
    public boolean estaDisponible(String horario,ArrayList<ReservaSalaVO> reservasSalaVO,ArrayList<ReservaEstacionVO> reservasEstacionVO){
        for(ReservaSalaVO reservaVO:reservasSalaVO)
            if(!horariosCompatibles(horario,reservaVO.getHorarioReserva()))
                return false;
        for(ReservaEstacionVO reservaVO:reservasEstacionVO)
            if(!horariosCompatibles(horario,reservaVO.getHorarioReserva()))
                return false;
        return true;
    }

    /**
     * <p>Verifica que el tiempo de reserva sea mayor que el minimo definido</p>
     * @param horario String con un horario en el formato hh:mm-hh:mm
     * @return true si el tiempo minimo se supera, false si no
     */
    public boolean tiempoMinimoReserva(String horario){
        Hora hInicio=new Hora(horario.substring(0,5));
        Hora hFin=new Hora(horario.substring(6));
        int minutosIni=hInicio.getHoras()*60+hInicio.getMinutos();
        int minutosFin=hFin.getHoras()*60+hFin.getMinutos();
        int diferenciaMinima=Restricciones.getMinimoBloquesRE()*Restricciones.getTamañoBloque();
        return minutosFin-minutosIni>=diferenciaMinima;
    }

    /**
     * <p>Verifica que el tiempo de reserva sea menor que el maximo definido</p>
     * @param horario String con un horario en el formato hh:mm-hh:mm
     * @return false si el tiempo maximo se supera, true si no
     */
    public boolean tiempoMaximoReserva(String horario){
        Hora hInicio=new Hora(horario.substring(0,5));
        Hora hFin=new Hora(horario.substring(6));
        int minutosIni=hInicio.getHoras()*60+hInicio.getMinutos();
        int minutosFin=hFin.getHoras()*60+hFin.getMinutos();
        int diferenciaMaxima=Restricciones.getMaximoBloquesRE()*Restricciones.getTamañoBloque();
        return minutosFin-minutosIni<=diferenciaMaxima;
    }

    /**
     * <p>Verifica si la fecha pasada como argumento tiene una diferencia minima 
     * de DIAS_MINIMOS_RESERVA_SALA con la fecha actual.</p>
     * @param fechaReserva Date con la fecha de una reserva
     * @return true si cumple con la diferencia minima, false si no
     */
    public boolean diasMinimosReserva(Date fechaReserva){
        Date fechaHoy=new Date();
        Date fechaMinima=new Date(fechaHoy.getYear(),fechaHoy.getMonth(),fechaHoy.getDate()+Restricciones.getTiempoMinimoRS()-1);
        return fechaReserva.after(fechaMinima);
    }

    /**
     * <p>Toma una ReservaSala y devuelve su version VO</p>
     * @param reservaSala ReservaSala
     * @return reservaSalaVO con la misma informacion del argumento
     */
    private ReservaSalaVO reservaSala_a_VO(ReservaSala reservaSala){
        ReservaSalaVO reservaSalaVO=new ReservaSalaVO();
        reservaSalaVO.setAdministrador(reservaSala.getAdministrador().getNombre());
        reservaSalaVO.setHorarioPrestamo(reservaSala.getHorarioPrestamo());
        reservaSalaVO.setHorarioReserva(reservaSala.getHorarioReserva());
        reservaSalaVO.setId(reservaSala.getId());
        reservaSalaVO.setProfesor(reservaSala.getProfesor());
        reservaSalaVO.setSala(reservaSala.getSala().getNombre());
        return reservaSalaVO;
    }

    /**
     * <p>Toma una reservaEstacion y devuelve su version VO</p>
     * @param reservaEstacion ReservaEstacion
     * @return reservaEstacionVO con la misma informacion del argumento
     */
    private ReservaEstacionVO reservaEstacion_a_VO(ReservaEstacion reservaEstacion){
        ReservaEstacionVO reservaEstacionVO=new ReservaEstacionVO();
        reservaEstacionVO.setUsuario(reservaEstacion.getUsuario().getCorreo());
        reservaEstacionVO.setEstacion(reservaEstacion.getEstacion().getId());
        reservaEstacionVO.setHorarioPrestamo(reservaEstacion.getHorarioPrestamo());
        reservaEstacionVO.setHorarioReserva(reservaEstacion.getHorarioReserva());
        return reservaEstacionVO;
    }
    
    /**
     * <p> busca una reserva en la base de datos que tenga el id
     * igial al de el objeto que recibe esta funcion</p>
     * @param rvo ReservaSalaVO
     * @param em EntityManager
     * @return objeto con informacion basica de la reserva de sala
     */
    public ReservaSalaVO ConfirmarReserva(ReservaSalaVO rvo, EntityManager em) {
        ReservaSalaDAO rsdao = new ReservaSalaDAO();
        ReservaSala rs = rsdao.buscar(rvo.getId(), em);
        if(rs==null){
            return null;
        }
        rs.setHorarioPrestamo(ServicioReservaEstacion.horaActual(0)+"---:--");
        try{
            rsdao.actualizar(rs, em);
        }catch(PersistenceException e){
            return rvo;
        }
        rvo.setEstado(Mensajes.OCUPADA);
        rvo.setHorarioPrestamo(rs.getHorarioPrestamo());
        return rvo;
    }

    public boolean devolverSala(long idReservaSala, EntityManager em) {
        ReservaSalaDAO rsdao = new ReservaSalaDAO();
        ReservaSala rs = null;
        try {
            rs = rsdao.buscar(idReservaSala, em);
        } catch (PersistenceException e){
            return false;
        }
        if(rs!=null){
            rs.setHorarioPrestamo(rs.getHorarioPrestamo().substring(0, 6)+ServicioReservaEstacion.horaActual(0));
            try {
                rsdao.actualizar(rs, em);
            } catch (PersistenceException e) {
                return false;
            }
            return true;
        }
        else{
            return false;
        }
    }

    /**
     * <p>Clase Hora para el manejo de horas</p>
     */
    class Hora{
        int horas;
        int minutos;
        public Hora(String hora){
            try{
                horas=Integer.parseInt(hora.substring(0,2));
                minutos=Integer.parseInt(hora.substring(3));
            }catch(NumberFormatException n){
                horas=minutos=0;
            }
        }
        public int getHoras(){
            return horas;
        }
        public int getMinutos(){
            return minutos;
        }
        public boolean esMenor(Hora hora){
            if(horas>hora.horas)
                return false;
            if(horas<hora.horas)
                return true;
            if(minutos>hora.minutos)
                return false;
            if(minutos<hora.minutos)
                return true;
            return false;
        }
        public boolean esMayor(Hora hora){
            if(esIgual(hora))
                return false;
            return !esMenor(hora);
        }
        public boolean esIgual(Hora hora){
            return horas==hora.horas && minutos==hora.minutos;
        }
    }

}